#include "OiPCH.hpp"
#include "Util/OiEthernet.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        class EthernetTaskImpl : public Impl<EthernetTask>
        {
        public:
            void init();

        public:
            QByteArray _readData;
            QList<QByteArray> _wDatas;
            QMutex _mutex;
            bool _quit;
            bool _ready;
        public:
            bool _simulation;
            bool _ethernetEnable;
            QString _type;
            QString _addr;
            QString _port;
            QTimer _test;
            int _times;
        };

        void EthernetTaskImpl::init()
        {
            OI_Q(EthernetTask);
            _quit = false;
            _ready = false;
        }

        EthernetTask::EthernetTask(QObject* parent) : QThread(parent)
        {
            OI_I(EthernetTask)->init();
        }

        EthernetTask::~EthernetTask()
        {
            OI_E(EthernetTask);
        }

        void EthernetTask::init(bool enable, QString type, QString addr, QString port)
        {
            OI_F(EthernetTask);

            auto settings = Core::OiSettings();
            d->_simulation = PLScanner::instance()->simulation;
            d->_ethernetEnable = enable;
            d->_type = type;
            d->_addr = addr;
            d->_port = port;

            if (!d->_simulation && d->_ethernetEnable)
            {
                start();
            }
        }

        void EthernetTask::exit()
        {
            OI_F(EthernetTask);

            if (isRunning())
            {
                quit();
                wait();
            }
        }

        bool EthernetTask::connected()
        {
            return OI_IMPL()->_ready;
        }

        void EthernetTask::run()
        {
            OI_F(EthernetTask);

            bool quit = false;
            int action;
            QByteArray wBuffer;
            QByteArray rBuffer;
            d->_times = 0;
            if (d->_simulation || !d->_ethernetEnable)
            {
                return;
            }

            QString ethernet_Name = d->_addr + ":" + d->_port;

            QSharedPointer<Util::Ethernet>  device = Util::EthernetService::instance()->get(ethernet_Name.toStdString().c_str());

            if (device.isNull())
            {
                if (d->_type == QLatin1Literal("TCP"))
                {
                    device = Util::Ethernet::createTcp(d->_addr.toStdString().c_str(), d->_port.toInt(), Util::Client);
                }
                else
                {
                    device = Util::Ethernet::createUdp(d->_addr.toStdString().c_str(), d->_port.toInt());
                }

                Util::EthernetService::instance()->add(ethernet_Name.toStdString().c_str(), device);
            }

            while (!quit)
            {
                d->_mutex.lock();
                rBuffer.clear();
                wBuffer.clear();
                quit = d->_quit;
                action = d->_wDatas.size();

                if (action != 0)
                {
                    wBuffer = d->_wDatas.front();
                    d->_wDatas.pop_front();
                }

                d->_mutex.unlock();

                if (!device->connect())
                {

                    d->_mutex.lock();
                    d->_wDatas.clear();
                    d->_mutex.unlock();
                    PLScannerError::instance()->reportError(PLScannerError::ERR_ETHERNET_DISCONNECTED);
                    OiWarning() << "ERR_ETHERNET_DISCONNECTED:" <<ethernet_Name;
                    Sleep(1000);
                    if (d->_times<4)
                    {
                        if (d->_type == QLatin1Literal("TCP"))
                        {
                            device = Util::Ethernet::createTcp(d->_addr.toStdString().c_str(), d->_port.toInt(), Util::Client);
                        }
                        else
                        {
                            device = Util::Ethernet::createUdp(d->_addr.toStdString().c_str(), d->_port.toInt());
                        }

                        Util::EthernetService::instance()->add(ethernet_Name.toStdString().c_str(), device);
                    }
                    d->_ready = false;
                    d->_times++;
                    continue;
                }
                else
                {
                    PLScannerError::instance()->clearError(PLScannerError::ERR_ETHERNET_DISCONNECTED);
                }

                if (!device->isValid())
                {
                    PLScannerError::instance()->reportError(PLScannerError::ERR_ETHERNET_INVAILD);
                    d->_ready = false;

                    break;
                }
                else
                {
                    PLScannerError::instance()->clearError(PLScannerError::ERR_ETHERNET_INVAILD);

                    if (action != 0 && !wBuffer.isEmpty())
                    {
                        if (device->writeBytes(wBuffer/*, 1000*/))
                        {
                            //OiWarning() << "Write:" << wBuffer;
                        }
                        else
                        {
                            OiWarning() << "TCP ERROR" << device->error();
                        }

                    }
                    else
                    {
                        if (device->readBytes(rBuffer, -1, 100))
                        {
                            //OiWarning() << "Read:" << rBuffer;

                            d->_mutex.lock();
                            d->_readData = rBuffer;
                            d->_mutex.unlock();

                            emit readReady(d->_readData);
                        }
                    }

                    d->_ready = true;
                }
            }
        }

        void EthernetTask::quit()
        {
            OI_F(EthernetTask);

            QMutexLocker lock(&d->_mutex);
            d->_quit = true;
        }

        void EthernetTask::start()
        {
            OI_F(EthernetTask);
            d->_mutex.lock();
            d->_quit = false;
            d->_wDatas.clear();
            d->_mutex.unlock();
            QThread::start();
        }
    

        bool EthernetTask::write(QByteArray data)
        {
            OI_F(EthernetTask);

            if (d->_simulation || !d->_ethernetEnable)
            {
                return true;
            }

            if (!isRunning())
            {
                start();
            }

            QMutexLocker lock(&d->_mutex);
            d->_wDatas.push_back(data);

            return true;
        }

        QByteArray EthernetTask::read()
        {
            OI_F(EthernetTask);

            QMutexLocker lock(&d->_mutex);
            return d->_readData;
        }

    }
}
